package org.example;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

//TODO 现在的动态代理仅限于org.example包下 写死了
//用于生成代理类的工厂
public class MyInterfaceFactory {

    static final String PACKAGE_NAME = "org.example";

    //模拟生成代理类名 暂未用到
    static int count = 1;//什么时候加不知道

    private static String getProxyName() {
        return "MyProxy" + count;
    }



    //加载编译好的class 并返回实例
    public static Object getInstance(Class<?> interfaceClass, MyInvocationHandler handler) {
        try {
            //TODO 要有机制 防止重复编译  如果是单例模式 则要加缓存机制  这里暂时不写了
            compile(createFile(interfaceClass));
            Class<?> clazz = Class.forName(PACKAGE_NAME + "." + getProxyName());
            Constructor<?> constructor = clazz.getConstructor(MyInvocationHandler.class);
            count++;
            return constructor.newInstance(handler);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //编译文件   要放进classpath中
    public static void compile(File javaFilePath) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        // 输出目录（就这样写死）
        File outputDir = new File("target/classes");

        // 确保目录存在
        if (!outputDir.exists()) {
            outputDir.mkdirs();
        }

        // 使用 -d 参数指定输出目录
        int result = compiler.run(null, null, null,
                "-d", outputDir.getPath(), javaFilePath.getPath());

        if (result == 0) {
            System.out.println("编译成功！");
        } else {
            System.out.println("编译失败！");
        }
    }


    //创建文件
    public static File createFile(Class<?> interfaceClass) {
        String context = createFileString(interfaceClass);

        File file = new File("src/main/java/org/example/" + getProxyName() + ".java");
        //把字符串写入文件中
        try {
            java.io.FileWriter writer = new java.io.FileWriter(file);
            writer.write(context);
            writer.close();
        } catch (java.io.IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println(file);
            return file;
        }


    }


    //动态生成代理类的字符串
    public static String createFileString(Class<?> interfaceClass) {

        // 生成代理类的源代码
        StringBuilder code = new StringBuilder();

        // 包和类定义
        code.append("package org.example;\n\n");
        code.append("public class ").append(getProxyName()).append(" implements ").append(interfaceClass.getSimpleName()).append(" {\n");

        // 声明 MyInvocationHandler 成员变量
        code.append("    private MyInvocationHandler handler;\n\n");

        // 构造函数
        code.append("    public ").append(getProxyName()).append("(MyInvocationHandler myInvocationHandler) {\n");
        code.append("        this.handler = myInvocationHandler;\n");
        code.append("    }\n\n");

        // 生成方法
        for (Method method : interfaceClass.getMethods()) {
            code.append("    @Override\n");
            code.append("    public ").append(method.getReturnType().getSimpleName()).append(" ").append(method.getName()).append("() {\n");

            if (method.getReturnType() == void.class) {
                code.append("        try {\n");
                code.append("            handler.invoke(this, ").append(interfaceClass.getName()).append(".class.getMethod(\"").append(method.getName()).append("\"), null);\n");
                code.append("        } catch (Throwable e) {\n");
                code.append("            throw new RuntimeException(e);\n");
                code.append("        }\n");
            } else {
                code.append("        try {\n");
                code.append("            Object result = handler.invoke(this, ").append(interfaceClass.getName()).append(".class.getMethod(\"").append(method.getName()).append("\"), null);\n");
                code.append("            return (").append(method.getReturnType().getSimpleName()).append(") result;\n");
                code.append("        } catch (Throwable e) {\n");
                code.append("            throw new RuntimeException(e);\n");
                code.append("        }\n");
            }

            code.append("    }\n\n");
        }

        code.append("}\n");

        return code.toString();
    }



}
